<template>
  <div class="basic-table-wrapper" :class="{ 'auto-size': autoSize }">
    <BasicForm
      ref="basicFormRef"
      :hideSearchFormReset="hideSearchFormReset || false"
      v-if="useSearchForm && Array.isArray(useSearchForm) && useSearchForm.length"
      :formSchema="useSearchForm"
      @search="changeSearchForm"
    >
      <template #formActionLeft>
        <slot name="formActionLeft"></slot>
      </template>
      <template #actions="{ record }">
        <slot name="searchFormActions" :formData="record"></slot>
      </template>
    </BasicForm>
    <slot name="underForm"></slot>
    <el-table
      v-loading="tableLoading"
      class="basic-table"
      ref="tableRef"
      :data="dataSource"
      v-bind="extend"
      style="width: 100%"
      @select-all="handleSelectAll"
      @selection-change="handleSelectionChange"
    >
      <el-table-column
        v-if="expandColumn && typeof expandColumn === 'object'"
        v-bind="expandColumn"
        type="expand"
      >
        <template #default="scope">
          <slot name="expand" :record="scope"></slot>
        </template>
      </el-table-column>
      <el-table-column
        v-if="selectionColumn && typeof selectionColumn === 'object'"
        v-bind="selectionColumn"
        type="selection"
      />
      <el-table-column
        v-for="(item, index) in columns"
        :key="`${item.prop}_${index}`"
        v-bind="item"
      >
        <template #default="scope">
          <span
            v-if="item.formatter"
            v-text="item.formatter(scope.row, scope.column, scope.row[item.prop], scope.$index)"
          ></span>
          <span
            v-else-if="!item.slots"
            v-text="
              scope.row[item.prop] ? scope.row[item.prop] : scope.row[item.prop] === 0 ? 0 : '-'
            "
          ></span>
          <slot v-else :record="scope" :name="item.slots.name" :index="index"></slot>
        </template>
        <template v-if="item.tips" #header>
          <div class="header-cell" :style="{ justifyContent: item.align || 'left' }">
            <span>{{ item.label }}</span>
            <el-tooltip effect="dark" :content="item.tips" placement="top-start">
              <div class="icon-doubt">
                <span>?</span>
              </div>
            </el-tooltip>
          </div>
        </template>
      </el-table-column>
      <el-table-column
        v-if="actionColumn && typeof actionColumn === 'object'"
        :label="actionColumn.label"
        :prop="actionColumn.prop"
        :width="actionColumn.width || 0"
        v-bind="actionColumn"
      >
        <template #default="scope">
          <slot name="actions" :record="scope"></slot>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
      v-if="getPagination && getPagination.isShow"
      class="basic-table-pagination"
      v-bind="getPagination"
      @size-change="changePageSize"
      @current-change="changeCurrentPage"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { formatDate } from '@/utils/formatTime'
import { BasicForm } from '@/components/BasicForm/index'
import type { FormSchemaItem } from '@/components/BasicForm/src/types'
import { filterNullOrUndefinedFields } from '@/utils/index'

defineOptions({ name: 'BasicTable' })

interface ColumnItem {
  prop: string
  label: string
  width?: number | string
  formatter?: (row: any, column: any, cellValue: any, index: number) => string | import('vue').VNode
  slots?: { name: string }
  tips?: string
  align?: string
  [key: string]: any
}

const props = withDefaults(
  defineProps<{
    api?: Function
    useSearchForm?: FormSchemaItem[] | boolean
    hideSearchFormReset?: boolean
    datasource?: any[] | boolean
    columns?: ColumnItem[]
    extend?: Record<string, any>
    immediate?: boolean
    responseStructure?: Record<string, any>
    actionColumn?: Record<string, any> | boolean
    expandColumn?: Record<string, any> | boolean
    selectionColumn?: Record<string, any> | boolean
    pagination?: Record<string, any> | boolean
    autoSize?: boolean
    beforeFetch?: (params: Record<string, any>) => Promise<void>
    afterFetch?: (dataSource: any[], res: any) => void
  }>(),
  {
    api: () => Promise.resolve({ data: [], total: 0 }),
    useSearchForm: false,
    hideSearchFormReset: false,
    datasource: false,
    columns: () => [],
    extend: () => ({}),
    immediate: true,
    responseStructure: () => ({}),
    actionColumn: false,
    expandColumn: false,
    selectionColumn: false,
    pagination: true,
    autoSize: false,
    beforeFetch: () => Promise.resolve(),
    afterFetch: () => {}
  }
)
const emit = defineEmits(['select-all', 'selection-change'])

const formFields = computed(() => {
  if (props.useSearchForm && Array.isArray(props.useSearchForm) && props.useSearchForm.length) {
    return (props.useSearchForm as any[]).reduce((acc, item) => {
      acc[item.field] = null
      return acc
    }, {})
  } else {
    return {}
  }
})
const getPagination = computed(() => {
  if (props.pagination) {
    let obj = {
      ...pagination_data.value
    }
    if (typeof props.pagination === 'object') {
      obj = {
        ...obj,
        ...props.pagination
      }
    }
    return obj
  } else {
    return false
  }
})
const getResponseStructure = computed(() => {
  if (props.responseStructure && typeof props.responseStructure === 'object') {
    return {
      ...responseStructure_data.value,
      ...props.responseStructure
    }
  } else {
    return false
  }
})

const formData = ref({ ...formFields.value })
const dataSource = ref<any[]>([])
const pagination_data = ref({
  total: 0,
  pageSize: 10,
  currentPage: 1,
  pageSizes: [10, 20, 50, 100, 0, 300, 400],
  layout: 'total, sizes, prev, pager, next, jumper',
  pageSizeString: 'pageSize',
  pageString: 'pageNo',
  isShow: true
})
const responseStructure_data = ref({
  data: 'data.list',
  total: 'data.total',
  code: 'code',
  msg: 'msg'
})
const tableLoading = ref(false)
const basicFormRef = ref()
const tableRef = ref()

function reload(param = { page: null, pageSize: null }) {
  const { page: pageParam, pageSize: pageSizeParam } = param

  if (pageParam) {
    pagination_data.value.currentPage = pageParam
  } else {
    delete (param as any).page
  }
  if (pageSizeParam) {
    pagination_data.value.pageSize = pageSizeParam
  } else {
    delete (param as any).pageSize
  }
  const { data: dataPath, total: totalPath } = getResponseStructure.value as any
  const { pageString, pageSizeString } = getPagination.value as any
  let { currentPage: pageData, pageSize: pageSizeData } = pagination_data.value

  if (props.beforeFetch) {
    props.beforeFetch(formData.value)
  }
  tableLoading.value = true
  if (props.datasource && Array.isArray(props.datasource)) {
    dataSource.value = props.datasource as any
    pagination_data.value.total = props.datasource.length
    tableLoading.value = false
  } else if (props.api != null) {
    let paramsObj = {
      ...filterNullOrUndefinedFields(formData.value),
      ...param
    }
    if (props.pagination && typeof props.pagination === 'object') {
      paramsObj[pageString] = pageData
      paramsObj[pageSizeString] = pageSizeData
    }
    console.log('paramsObj++++00000', paramsObj)
    props
      .api(paramsObj)
      .then((res: any) => {
        console.log(res, 'res', dataPath, totalPath)
        // dataSource.value = onjectKeyPathValue(res, dataPath)
        dataSource.value = res?.list || res.data?.list || res.data || res
        pagination_data.value.total = res?.total || res.data?.total || res.total
        tableLoading.value = false
        if (props.afterFetch) {
          props.afterFetch(dataSource.value, res)
        }
      })
      .catch(() => {
        tableLoading.value = false
      })
  }
}

function changeSearchForm(params: any) {
  formData.value = params
  pagination_data.value.currentPage = 1
  reload()
}
function changePageSize(pageSize: number) {
  pagination_data.value.pageSize = pageSize
  reload()
}
function changeCurrentPage(current: number) {
  pagination_data.value.currentPage = current
  reload()
}
function setSearchFormOption(paramsArr: any[]) {
  return new Promise((resolve) => {
    if (!paramsArr.length) {
      resolve({ code: 0, data: [], msg: '参数为空数组' })
    } else {
      let resArr: any[] = []
      paramsArr.map((item, index) => {
        if (item.field && (item.value || item.value === 0)) {
          ;(basicFormRef.value as any)?.setFormItemOption(item)
          resArr.push(item)
        }
        if (index == paramsArr.length - 1) {
          resolve({ code: 0, data: resArr, msg: '数据处理完毕，处理结果由data回显' })
        }
      })
    }
  })
}
function setSearchFormValue(paramsArr: any[]) {
  return new Promise((resolve) => {
    if (!paramsArr.length) {
      resolve({ code: 0, data: [], msg: '参数为空数组' })
    } else {
      let resArr: any[] = []
      paramsArr.map((item, index) => {
        if (item.field) {
          ;(basicFormRef.value as any)?.setFormItemValue(item)
          formData.value = { ...formData.value, [item.field]: item.value }
          resArr.push(item)
        }
        if (index == paramsArr.length - 1) {
          resolve({ code: 0, data: resArr, msg: '数据处理完毕，处理结果由data回显' })
        }
      })
    }
  })
}
function onjectKeyPathValue(target: any, keyPath: string, value?: any) {
  if (!keyPath || typeof keyPath !== 'string') return target
  const keys = keyPath.split('.')
  let m = target
  for (let i = 0; i < keys.length - 1; i++) {
    m = m[keys[i]]
  }
  if (arguments.length === 3) {
    m[keys[keys.length - 1]] = value
    return
  }
  m = m[keys[keys.length - 1]]
  return m
}
function filterBinds(filterObj: Record<string, any>, filterArr: string[]) {
  return Object.keys(filterObj)
    .filter((key) => filterArr.indexOf(key) < 0)
    .reduce((obj, key) => {
      obj[key] = filterObj[key]
      return obj
    }, {})
}
function resetTable() {
  const form = basicFormRef.value
  pagination_data.value.currentPage = 1
  pagination_data.value.pageSize = 10
  form?.resetSearch()
}
function handleSelectAll(val: any) {
  emit('select-all', val, dataSource.value)
}
function handleSelectionChange(val: any) {
  emit('selection-change', val, dataSource.value)
}
function toggleSelection(rows?: any[]) {
  if (rows) {
    rows.forEach((row) => {
      ;(tableRef.value as any)?.toggleRowSelection(row)
    })
  } else {
    ;(tableRef.value as any)?.clearSelection()
  }
}

onMounted(() => {
  if (props.immediate) {
    reload()
  }
})

defineExpose({
  reload,
  setSearchFormOption,
  setSearchFormValue,
  resetTable,
  toggleSelection,
  dataSource
})
</script>

<style lang="scss">
.basic-table-wrapper {
  display: flex;
  flex-direction: column;

  & > div {
    flex: 0 0 auto;
  }

  &.auto-size {
    width: 100%;
    height: 100%;

    & > .basic-table {
      flex: 1 1 auto;
    }
  }
}

.basic-table-pagination {
  width: 100%;
  display: flex;
  justify-content: flex-end;
  text-align: right;
  margin-top: 20px;
}
.header-cell {
  display: flex;
  align-items: center;
  .icon-doubt {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1px solid #ccc;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-left: 6px;
    span {
      font-size: 12px;
      color: #ccc;
    }
  }
}
</style>
